/**
 * 任务队列工具模块
 * 提供强大的异步任务调度和队列管理功能，支持并发控制、批量处理等高级特性
 */

/**
 * 任务队列类
 * 提供异步任务的排队执行、并发控制、批量处理等功能
 * @template T - 任务返回类型
 * @class TaskQueue
 * @example
 * // 创建一个最大并发数为3的任务队列
 * const queue = new TaskQueue<Response>(3);
 * 
 * // 添加多个网络请求任务
 * queue.add(() => fetch('https://api.example.com/users/1'));
 * queue.add(() => fetch('https://api.example.com/users/2'));
 * queue.add(() => fetch('https://api.example.com/users/3'));
 * queue.add(() => fetch('https://api.example.com/users/4'));
 * // 前3个请求并行执行，第4个请求等待前3个中的一个完成后再执行
 */
export class TaskQueue<T> {
  /**
   * 任务队列数组，存储待执行的异步任务
   * @private
   * @type {Array<() => Promise<T>>}
   */
  private __queue: Array<() => Promise<T>> = [];

  /**
   * 队列是否正在处理任务的标志
   * @private
   * @type {boolean}
   */
  private __isProcessing = false;

  /**
   * 最大并发任务数
   * @private
   * @type {number}
   */
  private __maxConcurrency = 1;

  /**
   * 当前活跃的任务数量
   * @private
   * @type {number}
   */
  private __activeTasks = 0;

  /**
   * 队列为空时的回调函数
   * @private
   * @type {(() => void) | undefined}
   */
  private __onEmptyCallback?: () => void;

  /**
   * 创建一个新的任务队列实例
   * @constructor
   * @param {number} [maxConcurrency=1] - 最大并发任务数量，默认为1
   * @example
   * // 创建一个默认队列（最大并发数为1，即串行执行）
   * const serialQueue = new TaskQueue();
   * 
   * // 创建一个最大并发数为5的队列
   * const parallelQueue = new TaskQueue(5);
   */
  constructor(maxConcurrency: number = 1) {
    this.__maxConcurrency = Math.max(1, maxConcurrency);
  }

  /**
   * 添加单个异步任务到队列
   * 任务会在合适的时机执行，受队列的最大并发数限制
   * @param {() => Promise<T>} task - 要添加的任务
   * @returns {Promise<T>} 任务执行结果的Promise
   * @example
   * const queue = new TaskQueue<number>(2);
   * 
   * // 添加两个异步任务
   * const result1 = queue.process(async () => {
   *   await new Promise(resolve => setTimeout(resolve, 1000));
   *   return 1;
   * });
   * 
   * const result2 = queue.process(async () => {
   *   await new Promise(resolve => setTimeout(resolve, 500));
   *   return 2;
   * });
   * 
   * // 等待结果
   * result1.then(value => console.log(value)); // 1秒后输出: 1
   * result2.then(value => console.log(value)); // 0.5秒后输出: 2
   */
  process(task: () => Promise<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      this.__queue.push(() => {
        return new Promise<T>((taskResolve, taskReject) => {
          Promise.resolve()
            .then(() => task())
            .then((result) => {
              taskResolve(result);
              resolve(result);
            })
            .catch((error) => {
              taskReject(error);
              reject(error);
            });
        });
      });
      
      if (!this.__isProcessing) {
        this.__processQueue();
      }
    });
  }

  /**
   * 内部方法：处理队列中的任务
   * 该方法负责管理任务的执行、并发控制和队列状态维护
   * @private
   * @description
   * 1. 当队列中有任务且未达到最大并发数时，启动新任务
   * 2. 任务完成后更新活跃任务计数，并继续处理下一个任务
   * 3. 当队列为空且所有任务完成时，触发onEmpty回调（如果设置）
   */
  private __processQueue(): void {
    this.__isProcessing = true;

    // 只要队列中有任务且未达到最大并发数，就继续处理任务
    while (this.__queue.length > 0 && this.__activeTasks < this.__maxConcurrency) {
      const task = this.__queue.shift();
      if (task) {
        this.__activeTasks++;
        
        task()
          .finally(() => {
            this.__activeTasks--;
            
            // 检查队列是否为空且没有活跃任务
            if (this.__queue.length === 0 && this.__activeTasks === 0) {
              this.__isProcessing = false;
              if (this.__onEmptyCallback) {
                this.__onEmptyCallback();
              }
            } else {
              // 继续处理队列
              this.__processQueue();
            }
          });
      }
    }
  }

  /**
   * 获取队列中待执行的任务数量
   * 不包括已经开始执行的任务
   * @returns {number} 队列中的待执行任务数量
   * @example
   * const queue = new TaskQueue();
   * 
   * // 添加一些任务
   * queue.add(() => someAsyncOperation());
   * queue.add(() => anotherAsyncOperation());
   * 
   * console.log(queue.size); // 输出: 2
   */
  get size(): number {
    return this.__queue.length;
  }

  /**
   * 检查队列是否为空且没有正在执行的任务
   * @returns {boolean} 如果队列为空且没有活跃任务，返回true；否则返回false
   * @example
   * const queue = new TaskQueue();
   * console.log(queue.isEmpty); // 输出: true
   * 
   * queue.add(() => someAsyncOperation());
   * console.log(queue.isEmpty); // 输出: false（即使任务已开始执行）
   * 
   * // 当所有任务完成后
   * setTimeout(() => {
   *   console.log(queue.isEmpty); // 输出: true
   * }, 1000);
   */
  get isEmpty(): boolean {
    return this.__queue.length === 0 && this.__activeTasks === 0;
  }

  /**
   * 获取当前正在执行的任务数量
   * @returns {number} 当前活跃的任务数量
   * @example
   * const queue = new TaskQueue(2); // 最大并发数为2
   * 
   * // 添加两个会运行较长时间的任务
   * queue.add(() => new Promise(resolve => setTimeout(() => resolve(), 2000)));
   * queue.add(() => new Promise(resolve => setTimeout(() => resolve(), 2000)));
   * 
   * // 立即检查活跃任务数
   * setTimeout(() => {
   *   console.log(queue.activeCount); // 输出: 2
   * }, 100);
   * 
   * // 2秒后，任务应该完成
   * setTimeout(() => {
   *   console.log(queue.activeCount); // 输出: 0
   * }, 2100);
   */
  get activeCount(): number {
    return this.__activeTasks;
  }

  /**
   * 设置队列为空且所有任务完成时的回调函数
   * @param {() => void} callback - 队列为空时的回调函数
   * @example
   * const queue = new TaskQueue();
   * 
   * // 设置队列为空时的回调
   * queue.onEmpty(() => {
   *   console.log('所有任务已完成！');
   * });
   * 
   * // 添加一些任务
   * queue.add(() => someAsyncOperation1());
   * queue.add(() => someAsyncOperation2());
   * 
   * // 当所有任务完成且队列为空时，会触发回调
   * // 输出: '所有任务已完成！'
   */
  onEmpty(callback: () => void): void {
    this.__onEmptyCallback = callback;
    if (this.isEmpty && callback) {
      callback();
    }
  }

  /**
   * 清空队列中所有待执行的任务
   * 已经开始执行的任务不受影响
   * @returns {void}
   * @example
   * const queue = new TaskQueue();
   * 
   * // 添加几个任务
   * queue.add(() => fetch('https://api.example.com/endpoint1'));
   * queue.add(() => fetch('https://api.example.com/endpoint2'));
   * queue.add(() => fetch('https://api.example.com/endpoint3'));
   * 
   * // 清空队列
   * queue.clear();
   * console.log(queue.size); // 输出: 0
   */
  clear(): void {
    this.__queue = [];
  }

  /**
   * 批量添加任务到队列
   * 将一组数据项转换为异步任务并添加到队列中，利用队列的并发控制机制处理
   * @template U - 输入数据类型
   * @param {Array<U>} items - 数据项列表
   * @param {(item: U) => Promise<T>} processor - 数据处理函数，将每个数据项转换为异步任务
   * @returns {Promise<T[]>} - 所有任务结果按输入顺序排列的Promise数组
   * @example
   * const userIds = [1, 2, 3, 4, 5];
   * const queue = new TaskQueue<UserData>(2); // 限制最大并发数为2
   * 
   * // 批量添加获取用户数据的任务
   * const users = await queue.processAll(userIds, async (id) => {
   *   const response = await fetch(`https://api.example.com/users/${id}`);
   *   return response.json();
   * });
   * 
   * console.log(users); // 包含5个用户数据的数组，顺序与userIds对应
   */
  async processAll<U>(items: Array<U>, processor: (item: U) => Promise<T>): Promise<T[]> {
    const promises = items.map(item => {
      return this.process(() => processor(item));
    });
    return Promise.all(promises);
  }

  /**
   * 按批次处理任务
   * 将数据项分成多个批次，逐批次处理，每批次内的任务可并行执行
   * @template U - 输入数据类型
   * @param {Array<U>} items - 数据项列表
   * @param {number} batchSize - 每批处理的任务数量
   * @param {(item: U) => Promise<T>} processor - 数据处理函数
   * @returns {Promise<T[]>} - 所有任务结果按输入顺序排列的Promise数组
   * @example
   * const largeDataset = Array.from({length: 100}, (_, i) => i + 1); // 100个数据项
   * const queue = new TaskQueue<ProcessedData>(5); // 每批内最大并发数为5
   * 
   * // 按批次处理数据，每批10个
   * const results = await queue.processBatch(largeDataset, 10, async (item) => {
   * // 处理每个数据项
   * return processItem(item);
   * });
   * 
   * // 这将分10批处理100个数据项，每批10个，每批内最大5个并发
   */
  async processBatch<U>(
    items: Array<U>,
    batchSize: number,
    processor: (item: U) => Promise<T>
  ): Promise<T[]> {
    const results: T[] = [];
    const totalBatches = Math.ceil(items.length / batchSize);
    let currentBatch = 0;

    while (currentBatch < totalBatches) {
      const startIndex = currentBatch * batchSize;
      const endIndex = Math.min(startIndex + batchSize, items.length);
      const batchItems = items.slice(startIndex, endIndex);

      // 处理当前批次的所有任务
      const batchResults = await this.processAll(batchItems, processor);
      results.push(...batchResults);

      currentBatch++;
    }

    return results;
  }
}